#include "htmlexporter.h"

#include "../documents/data/reportdata.h"
#include "../documents/data/simgraphicdata.h"

#include <ilinkeddocument.h>

#include <QtCore/QFile>
#include <QtCore/QTextStream>
#include <QtCore/QUrl>
#include <QtGui/QStandardItemModel>

using namespace bignumbers;
using namespace core;
using namespace swp;

HtmlExporter::HtmlExporter() : IExporter()
{

}

HtmlExporter::~HtmlExporter()
{

}

void HtmlExporter::exportReport(const QUrl& url,
    const QSharedPointer<core::IDocument>& report) const
{
    const QString path = url.toLocalFile();

    QFile file(path);
    if (file.open(QFile::WriteOnly))
    {
        QTextStream stream(&file);

        QString content;

        const QSharedPointer<ILinkedDocument> _report =
            qSharedPointerDynamicCast<ILinkedDocument>(report);

        Q_ASSERT(!_report.isNull()); // bad document type
        Q_ASSERT(_report->links().size() == 1); // can't happen

        SimGraphicData* linkedData = dynamic_cast<SimGraphicData* >(
            _report->links().at(0)->data());

        const QRectF boundingRect = linkedData->itemsBoundingRect();

        QSize size;

        if ((boundingRect.width() > 800) || (boundingRect.height() > 500))
        {
            if (boundingRect.width() > boundingRect.height())
            {
                size.rwidth() = 800;
                size.rheight() = ((double)800 / boundingRect.width()) *
                    boundingRect.height();
            }
            else
            {
                size.rwidth() = ((double)500 / boundingRect.height()) *
                    boundingRect.width();
                size.rheight() = 500;
            }
        }
        else
        {
            size.rwidth() = boundingRect.width();
            size.rheight() = boundingRect.height();
        }

        QRectF target(0, 0, size.width(), size.height());

        QImage image(size, QImage::Format_ARGB32);
        QPainter painter(&image);
        linkedData->render(&painter, target, boundingRect);

        QString imgPath = path;
        imgPath = imgPath.replace(".html", ".png");

        QFile imgFile(imgPath);
        imgFile.open(QFile::WriteOnly | QFile::Truncate);

        image.save(&imgFile, "PNG");

        imgFile.close();

        ReportData* data = dynamic_cast<ReportData* >(report->data());

        Q_ASSERT(data != 0 && linkedData != 0); // bad data

        bool suc = true;
        const QString success = getSuccess(data->tokens(), suc);

        int runs;
        QString totalCost, avgCost, totalDuration, avgDuration;
        QHash<QString, QList<BigInt> > activityDetail;
        QHash<QString, QList<BigFloat> > activityDetailAvgs;

        getOverview(data->executions(), runs, totalCost, avgCost,
            totalDuration, avgDuration, activityDetail, activityDetailAvgs);

        content = QString("<div><img src=\"%1.png\" alt=\"\" /></div>").arg(
            report->name());
        content += QString("<table>"
            "<tr><td><span class=\"%9\">%1</span></td></tr>"
            "<tr><td>Report for:</td><td>%2</td></tr>"
            "<tr><td>Generated:</td><td>%3</td></tr>"
            "<tr><td></td></tr>"
            "<tr><td>Total cost:</td><td>%4</td></tr>"
            "<tr><td>Average cost (per run):</td><td>%5</td></tr>"
            "<tr><td></td></tr>"
            "<tr><td>Total duration:</td><td>%6</td></tr>"
            "<tr><td>Average duration (per run):</td><td>%7</td></tr>"
            "<tr><td></td></tr>"
            "<tr><td>Runned:</td><td>%8 times</td></tr>"
            "</table>").arg(success).arg(_report->links().at(0)->name()).arg(
                data->timeStamp()).arg(totalCost).arg(avgCost).arg(
                    totalDuration).arg(avgDuration).arg(runs).arg(
                        suc ? "success" : "failure");

        generateActivityDetailHtml(activityDetail, activityDetailAvgs,
            content);

        const QString style = "*{font-family:sans-serif;margin:0;padding:0;}"
            "#header{padding:10px;background-color:#2cadd2;color:#f7f7f7;"
                "font-size:25px;}"
            "#content{padding:10px}.success{color:green;}.failure{color:red;}"
            "table td{color:gray}table td+td{color:black;font-weight:bold;}"
            "table span{font-size:25px;font-weight:bold;}"
            "table td{padding:5px;}#adWrapper{margin-top:10px;}h2{font-size:15px;}"
            "#adWrapper > .header{background-color:#cccccc;color:white;padding:5px 10px}"
            "#adWrapper .content{border:2px solid #cccccc;padding:0 2px 2px 2px;}"
            "#adWrapper .item{margin-top:2px;background-color:#f7f7f7}"
            "#adWrapper .item .header{background-color:#e9e9e9;padding:5px 10px}"
            "#adWrapper table{margin-left: 15px;}"
            "#adWrapper table td{color:#666666;font-weight:normal;}"
            "#adWrapper table td.v{color:black;font-weight:bold;}";

        stream << "<!DOCTYPE html>";
        stream << "<html lang=\"en\">";
        stream << "<head>";
        stream << "<meta charset=\"utf-8\">";
        stream << QString("<title>%1</title>").arg(report->name());
        stream << QString("<style>%1</style>").arg(style);
        stream << "</head>";
        stream << "<body>";
        stream << QString("<div id=\"header\">%1</div>").arg(report->name());
        stream << QString("<div id=\"content\">%1</div>").arg(content);
        stream << "</body>";
        stream << "</html>";
    }

    file.close();
}

IExporter* HtmlExporter::copy()
{
    return new HtmlExporter();
}

QString HtmlExporter::getSuccess(const QStandardItemModel* model,
    bool& _success) const
{
    QString ret;

    bool success = true;
    QList<int> runsWithFailure;

    int currentRun = 1;

    for (int i = 0; i < model->rowCount(); ++i)
    {
        if (model->item(i, 0)->text().toInt() > currentRun)
        {
            ++currentRun;
        }

        if (model->item(i, 3)->text().toInt() == 1)
        {
            if (model->item(i, 2)->text() == "{}")
            {
                success = false;

                if (!runsWithFailure.contains(currentRun))
                {
                    runsWithFailure.append(currentRun);
                }
            }
        }
        else
        {
            if (model->item(i, 2)->text() != "{}")
            {
                success = false;

                if (!runsWithFailure.contains(currentRun))
                {
                    runsWithFailure.append(currentRun);
                }
            }
        }
    }

    ret = QString("%1").arg(success ? QObject::tr("Success") :
        QObject::tr("Failure with %1 runs failed").arg(
            runsWithFailure.size()));

    _success = success;

    return ret;
}

void HtmlExporter::getOverview(const QStandardItemModel* model, int& _runs,
    QString& _totalCost, QString& _avgCost,
    QString& _totalDuration, QString& _avgDuration,
    QHash<QString, QList<BigInt> >& activityDetail,
    QHash<QString, QList<BigFloat> >& activityDetailAvgs) const
{
    BigInt totalCost;
    BigInt runDuration;
    BigInt totalDuration;

    int currentRun = 1;

    for (int i = 0; i < model->rowCount(); ++i)
    {
        if (model->item(
            i, 0)->text().toInt() > currentRun)
        {
            ++currentRun;

            totalDuration = totalDuration + runDuration;
            runDuration = BigInt("0");
        }

        BigInt cost = BigInt(model->item(i, 2)->text());

        QStandardItem* executions = model->item(i, 3);

        BigInt duration;

        for (int j = 0; j < executions->columnCount(); ++j)
        {
            const QString val = executions->child(0, j)->text();
            duration = duration + BigInt(val);
        }

        BigInt columnCount = BigInt(QString::number(
            executions->columnCount()));

        totalCost = totalCost + (cost * columnCount);
        runDuration = runDuration + duration;

        const QString name = model->item(i, 1)->text();

        if (activityDetail.contains(name))
        {
            QList<BigInt>& act = activityDetail[name];

            act[0] = act[0] + columnCount;
            act[1] = act[1] + (cost * columnCount);
            act[2] = act[2] + duration;
        }
        else
        {
            QList<BigInt> act;

            act.append(columnCount);
            act.append((cost * columnCount));
            act.append(duration);

            activityDetail[name] = act;
        }
    }

    if (currentRun == 1)
    {
        totalDuration = runDuration;
    }

    BigFloat runs = BigFloat(QString::number(currentRun));

    BigFloat averageCost = BigFloat(totalCost) / runs;
    BigFloat averageDuration = BigFloat(totalDuration) / runs;

    foreach (const QString& activity, activityDetail.keys())
    {
        QList<BigInt>& detail = activityDetail[activity];

        QList<BigFloat> detailAvgs;

        detailAvgs.append((BigFloat(detail[0]) / runs));
        detailAvgs.append((BigFloat(detail[1]) / runs));
        detailAvgs.append((BigFloat(detail[2]) / runs));

        activityDetailAvgs[activity] = detailAvgs;
    }

    _runs = currentRun;

    _totalCost = totalCost.toString();
    _avgCost = averageCost.toString(2);
    _totalDuration = totalDuration.toString();
    _avgDuration = averageDuration.toString(2);
}

void HtmlExporter::generateActivityDetailHtml(
    QHash<QString, QList<BigInt> >& activityDetail,
    QHash<QString, QList<BigFloat> >& activityDetailAvgs,
    QString& content) const
{
    QString actDetail;

    foreach (const QString& activity, activityDetail.keys())
    {
        const QList<BigInt>& detail = activityDetail[activity];
        const QList<BigFloat>& detailAvg = activityDetailAvgs[activity];

        actDetail += QString("<div class=\"item\">"
            "<div class=\"header\"><h2>%1</h2><span></span>"
                "<div class=\"clear\"></div></div>"
            "<table>"
                "<tr><td>Duration:</td><td class=\"v\">%2</td><td></td>"
                    "<td>Cost:</td><td class=\"v\">%3</td><td></td>"
                    "<td>Executions:</td><td class=\"v\">%4</td></tr>"
                "<tr><td>Average duration (per run):</td>"
                    "<td class=\"v\">%5</td><td></td>"
                    "<td>Average cost (per run):</td>"
                    "<td class=\"v\">%6</td><td></td>"
                    "<td>Average executions (per run):</td>"
                    "<td class=\"v\">%7</td></tr>"
            "</table>"
            "</div>").arg(activity).arg(detail[2].toString()).arg(
                detail[1].toString()).arg(detail[0].toString()).arg(
                    detailAvg[2].toString(2)).arg(detailAvg[1].toString(2)).arg(
                        detailAvg[0].toString(2));
    }

    content += QString("<div id=\"adWrapper\">"
        "<div class=\"header\">Activity detail</div>"
        "<div class=\"content\">%1</div></div>").arg(
            actDetail);
}
